package com.htfyun.ft.fragment.engineer;

import android.os.Bundle;
import android.os.storage.StorageVolume;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;

import androidx.annotation.Nullable;

import com.htfyun.ft.R;
import com.htfyun.ft.common.FTUtils;
import com.htfyun.ft.common.SDCardInfo;
import com.htfyun.ft.fragment.BaseFragment;
import com.htfyun.ft.utils.DebugLog;
import com.htfyun.ft.utils.FileReadWrite;
import com.htfyun.ft.utils.TimeStringUtils;
import com.htfyun.ft.utils.executor.AppExecutors;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class FragmentHardDiskRW_bk extends BaseFragment {

    private SDCardInfo sdCardInfo;

    private volatile boolean isStarted = false;
    private final Object startLock = new Object();

    private static final int COUNT_1_G = 10;

    private Button btnStart;
    private Button btnDelete;
    private Button btnClearTxt;
    private TextView txtShow;

    private Spinner spinnerSize;
    private Spinner spinnerCount;

    @Override
    protected int getLayoutRes() {
        return R.layout.fragment_engineer_hard_disk_rw;
    }

    @Override
    protected void onInitView(@Nullable Bundle savedInstanceState) {

        btnStart = (Button) findViewById(R.id.btnStart);
        btnDelete = (Button) findViewById(R.id.btnDelete);
        btnClearTxt = (Button) findViewById(R.id.btnClearTxt);
        txtShow = (TextView) findViewById(R.id.txtShow);

        spinnerSize = (Spinner) findViewById(R.id.spinnerSize);
        spinnerCount = (Spinner) findViewById(R.id.spinnerCount);

        txtShow.setMovementMethod(ScrollingMovementMethod.getInstance());

        sdCardInfo = new SDCardInfo(mContext);

        btnStart.setText(R.string.engineer_hard_disk_start);
        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isStarted) {
                    stopTest();
                } else {
                    startTest();
                }
            }
        });

        btnClearTxt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                txtShow.setText("");
            }
        });

        btnDelete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                btnStart.setEnabled(false);
                spinnerCount.setEnabled(false);
                spinnerSize.setEnabled(false);
                btnDelete.setEnabled(false);

                doDeleteWritten();

                btnStart.setEnabled(true);
                spinnerCount.setEnabled(true);
                spinnerSize.setEnabled(true);
                btnDelete.setEnabled(true);
            }
        });

    }

    public void startTest() {

        synchronized (startLock) {
            if (isStarted) {
                return;
            }
            isStarted = true;
        }

        btnStart.setText(R.string.engineer_hard_disk_stop);

        spinnerCount.setEnabled(false);
        spinnerSize.setEnabled(false);
        btnDelete.setEnabled(false);

        doTest();
    }

    public void stopTest() {

        synchronized (startLock) {
            isStarted = false;
        }

        btnStart.setText(R.string.engineer_hard_disk_start);

        spinnerCount.setEnabled(true);
        spinnerSize.setEnabled(true);
        btnDelete.setEnabled(true);

    }

    private List<String> testPathList = new CopyOnWriteArrayList<>();
    private void doTest() {
        List<StorageVolume> hdVolumes = sdCardInfo.getPublicVolumesExceptSD();

        if (hdVolumes.isEmpty()) {
            String log = getString(R.string.engineer_hard_disk_not_found);
            updateText(log);
            return;
        }
        testPathList.clear();

        int[] sizes = getResources().getIntArray(R.array.hd_rw_size_values);
        int[] counts = getResources().getIntArray(R.array.hd_rw_count_values);
        int sizeSelectedPos = spinnerSize.getSelectedItemPosition();
        int countSelectedPos = spinnerCount.getSelectedItemPosition();

        for (StorageVolume vol : hdVolumes) {
            testPathList.add(sdCardInfo.getVolumePath(vol));
            AppExecutors.runOnIoThread(new WRTestRunnable(sdCardInfo.getVolumePath(vol),
                    sizes[sizeSelectedPos] * WRTestRunnable.ONE_G,
                    counts[countSelectedPos]));
        }

    }

    private void doDeleteWritten() {
        List<StorageVolume> hdVolumes = sdCardInfo.getPublicVolumesExceptSD();

        if (hdVolumes.isEmpty()) {
            String log = getString(R.string.engineer_hard_disk_not_found);
            updateText(log);
        }

        AppExecutors.runOnIoThread(new Runnable() {
            @Override
            public void run() {
                List<StorageVolume> hdVolumes = sdCardInfo.getPublicVolumesExceptSD();

                for (StorageVolume vol : hdVolumes) {
                    String path = sdCardInfo.getVolumePath(vol) + File.separator + "testRW";
                    DeleteFolder(path);
                }

                if (getActivity() != null) {
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            String log = getString(R.string.engineer_hard_disk_delete_over);
                            updateText(log);
                        }
                    });
                }
            }
        });

    }

    public boolean DeleteFolder(String sPath) {
        File file = new File(sPath);
        // 判断目录或文件是否存在
        if (!file.exists()) {  // 不存在返回 false
            return true;
        } else {
            // 判断是否为文件
            if (file.isFile()) {  // 为文件时调用删除文件方法
                return deleteFile(sPath);
            } else {  // 为目录时调用删除目录方法
                return deleteDirectory(sPath);
            }
        }
    }

    public boolean deleteFile(String sPath) {
        boolean flag = false;
        File file = new File(sPath);
        // 路径为文件且不为空则进行删除
        if (file.isFile() && file.exists()) {
            file.delete();
            flag = true;
        }
        return flag;
    }

    public boolean deleteDirectory(String sPath) {
        //如果sPath不以文件分隔符结尾，自动添加文件分隔符
        if (!sPath.endsWith(File.separator)) {
            sPath = sPath + File.separator;
        }
        File dirFile = new File(sPath);
        //如果dir对应的文件不存在，或者不是一个目录，则退出
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            return false;
        }
        boolean flag = true;
        //删除文件夹下的所有文件(包括子目录)
        File[] files = dirFile.listFiles();
        for (int i = 0; i < files.length; i++) {
            //删除子文件
            if (files[i].isFile()) {
                flag = deleteFile(files[i].getAbsolutePath());
                if (!flag) break;
            } //删除子目录
            else {
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag) break;
            }
        }
        if (!flag) return false;
        //删除当前目录
        if (dirFile.delete()) {
            return true;
        } else {
            return false;
        }
    }

    private void updateText(String log) {
        if (getActivity() == null) {
            return;
        }
        final String theLog = log;
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                String text = txtShow.getText().toString();
                if (text.length() > 4096 * 4) {
                    txtShow.setText("");
                    text = "";
                }
                text += theLog + "\n";
                txtShow.setText(text);
            }
        });
    }

    private void onWriteAction(String path, long time) {
        String log = "write-> path = " + path + ", 用时: " + TimeStringUtils.makeTimeString(time);
        updateText(log);
    }


    private void onReadAction(String path,  long time) {
        String log = "read-> path = " + path + ", 用时: " + TimeStringUtils.makeTimeString(time);
        updateText(log);
    }

    public final static String PATH_READ_ERROR = FTUtils.getFTStorageDirectory() + File.separator + "hdError.log";

    private void onReadError(String path, String log) {
        String error = "read-> path = " + path + ", log: " + log;
        updateText(error);
        FileReadWrite.writeAppendFile(PATH_READ_ERROR, log);

    }

    private void onRWStartAction(String rootPath) {
        String log = "RW start-> path = " + rootPath;
        updateText(log);
    }

    private void onRWStopAction(String rootPath) {
        String log = "RW completed-> path = " + rootPath;
        updateText(log);

        testPathList.remove(rootPath);
        if (testPathList.isEmpty()) {
            onTestOver();
        }
    }

    private void onTestOver() {
        if (getActivity() == null) {
            return;
        }
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
              stopTest();
            }
        });
    }

    private final class WRTestRunnable implements Runnable {

        private final static int MAX_BYTE = 256;
        public final static int ONE_M = 1024 * 1024;//1M = 1024 * 1024 bytes
        public final static long ONE_G = 1024L * ONE_M;

        private final String rootPath;
        private final long size;
        private final int count;

        public WRTestRunnable(String rootPath, long size, int count) {
            this.rootPath = rootPath;
            this.size = size;
            this.count = count;

        }
        @Override
        public void run() {

            onRWStartAction(getRootPath());

            testWrite();
            testRead();

            onRWStopAction(getRootPath());

        }

        public String getRootPath() {
            return rootPath;
        }

        private String getDirPath() {
            String path = rootPath + File.separator + "testRW";
            File file = new File(path);
            if (!file.exists()) {
                file.mkdirs();
            }

            return path;
        }

        private void testWrite() {

            long start = System.currentTimeMillis();

            for (int i = 0; i < count; i++) {

                String filePath = getDirPath() + File.separator  + (i + ".bin");
                checkFile(filePath);
                synchronized (startLock) {
                    if (!isStarted) {
                        break;
                    }
                }

                writeData(new File(filePath), size);


                synchronized (startLock) {
                    if (!isStarted) {
                        break;
                    }
                }
            }

            long end = System.currentTimeMillis();

            onWriteAction(getRootPath(),  end - start);

        }

        private void testRead() {

            long start = System.currentTimeMillis();

            for (int i = 0; i < count; i++) {

                String filePath = getDirPath() + File.separator  + (i + ".bin");
                checkFile(filePath);

                synchronized (startLock) {
                    if (!isStarted) {
                        break;
                    }
                }


                readData(new File(filePath));

                synchronized (startLock) {
                    if (!isStarted) {
                        break;
                    }
                }
            }
            long end = System.currentTimeMillis();

            onReadAction(getRootPath(), end - start);

        }

        private byte[] readBuffer = new byte[ONE_M];

        private void readData(File file) {

            InputStream read = null;
            try {

                read = new FileInputStream(file);

                int length = 0;
                int cycleCount = 0;

                while ((length = read.read(readBuffer)) != -1) {

                    if (length == readBuffer.length) {

                        for (int i = 0; i < length;) {

                            for (int j = 0; j < 256; j++) {

                                if (j != (0xff & readBuffer[i])) {

                                    String error = ("read: test error->"
                                            + ", j = 0x" + Integer.toHexString((0xff & j))
                                            + ", buf[" + cycleCount * readBuffer.length + i + "] = 0x" + Integer.toHexString((0xff & readBuffer[i])));

                                    onReadError(file.getPath(), error);
                                }

                                i++;
                            }
                        }
                    }

                    cycleCount++;

                    synchronized (startLock) {
                        if (!isStarted) {
                            break;
                        }
                    }
                }

            } catch (Exception e) {
                DebugLog.e("Exception = " + e.getMessage());
                e.printStackTrace();

            } finally {

                for (int i = 0; i < 10; i++) {
                    System.gc();
                }

                if (read != null) {
                    try {
                        read.close();
                        read = null;
                    } catch (Exception e1) {
                        DebugLog.e("Exception = " + e1.getMessage());
                        e1.printStackTrace();
                    }

                }

            }
        }


        private byte[] writeOneBlockBuffer = new byte[ONE_M];

        private void writeData(File file, long totalSize) {


            for (int i = 0; i < writeOneBlockBuffer.length;) {
                for (int j = 0; j < MAX_BYTE; j++) {
                    writeOneBlockBuffer[i++] = (byte)j;
                }
            }

            OutputStream writer = null;

            try {

                writer = new FileOutputStream(file, true);

                long size = file.length();

                while (size < totalSize) {

                    writer.write(writeOneBlockBuffer);

                    writer.flush();

                    size = file.length();

                    synchronized (startLock) {
                        if (!isStarted) {
                            break;
                        }
                    }
                }

            } catch (Exception e) {
                DebugLog.e("Exception = " + e.getMessage());
                e.printStackTrace();

            } finally {

                for (int i = 0; i < 10; i++) {
                    System.gc();
                }
                if (writer != null) {
                    try {
                        writer.close();
                        writer = null;
                    } catch (Exception e1) {
                        DebugLog.e("Exception = " + e1.getMessage());
                        e1.printStackTrace();
                    }

                }
            }
        }


        private void checkFile(String filePath) {
            File file = new File(filePath);

            if (!file.exists()) {
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }




}
